iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Modern Web

從 Next.js 開始的 Functional Programming系列 第 17

D17 - 實作異步流程 (三)

  • 分享至 

  • xImage
  •  

今天跟大家介紹如何組合、重用 @effect/schema

Struct

要描述一個 Product type ,我們可以使用 S.struct,例如下面這樣

import * as S from '@effect/schema/Schema'

const adminSchema = S.struct({
  _tag: S.literal('Administrator'),
  name: S.string,
})

/*
{
  _tag: 'Administrator',
  name: string
}
*/

struct 的每個 key 後面可以是任意一種 shema

const nameSchema = S.struct({
  first: S.string,
  last: S.string,
})

const adminSchema = S.struct({
  _tag: S.literal('Administrator'),
  name: nameSchema
})

/*
{
	_tag: 'Administrator',
	name: {
		fitst: string,
		last: string
	}
}
*/

用起來就像 js 的 Record 一樣,非常直覺

Array & Tuple

Record 當然也就會有 ArrayTuple

const usersSchema = S.array(userSchema) // User []

const couple = S.tuple(userSchema, userSchema) // [User, User]

Pick, Omit, and Extend

有時候我們只是想在別的已經定義好的型別的基礎上,做一些小改變,而不想要重新新增一個相似度非常高的型別的時候,以上三個工具就非常的好用。

const nameSchema = S.struct({
	first: S.string,
	last: S.string,
})

/*
{
	fitst: string,
	last: string
}
*/

const newNameSchema = pipe(
	nameSchema,
	S.extend(S.struct({
		middle: S.string
	}))
)

/*
{
	first: string,
	middle: string,
	last: string
}
*/

const firstNameSchema = pipe(
	nameSchema,
	S.pick('first')
)

/*
{
	fitst: string,
}
*/

const lastNameSchema = pipe(
	nameSchema,
	S.omit('first')
)

/*
{
	last: string,
}
*/

Union

const adminSchema = S.struct({
  _tag: S.literal('Administrator'),
  name: S.string,
})

const memberSchema = S.struct({
  _tag: S.literal('Member'),
  name: S.string,
})

const user = S.union(adminSchema, memberSchema)
/*
{
	_tag: "Administrator",
	name: string
} | {
	_tag: "Member",
	name: string
}
*/

型別推演

其實定義 schema 的過程很像是定義型別。要是需要使用型別的時候,也要重複再寫一次類似的結構,就顯得有些麻煩了。幸運的是 @effect/schema 提供了我們轉換的工具。

const userSchema = S.struct({
	name: S.string,
	birth: S.Date,
})

type UserFrom = S.Schema.From<typeof userSchema>
/*
{
	name: string,
	birth: string
}
*/

type UserTo = S.Schema.From<typeof userSchema>
/*
{
	name: string,
	birth: Date
}
*/

我們在 D06 - 測試驅動開發曾經提到過藉由 Data.Case 的擴展,我們能比對兩個物件是否相等,並且自動產生型別。

Data.Case 可以讓原本不支援物件比對的 JS 物件,變得可以比對

import { Equal } from 'effect'
const result = Equal.equals(textInput1)(textInput)

更重要的是可以幫你產生 constructor

const ofValid = Data.tagged<ValidTextInput>('ValidTextInput')
const courseName1 = ofValid({ value: 'HTML/CSS/JS' })

可是如果使用上面的方式,解析出來的型別,是無法具有 Data.Case 性質的。

沒有 Data.case ,我們就無法進行 Equal 的比對,也沒辦法有自動生成的 constructor,真的很可惜,所以明天我們會介紹如何使用 @effect/schemaClass 來自動幫我們完成以下這些事情。

  • 定義型別
  • 定義檢查器、篩選器、轉換器
  • 自動產生 constructor
  • 提供比對功能

上一篇
D16 - 實作異步流程 (二)
下一篇
D18 - 實作異步流程 (四)
系列文
從 Next.js 開始的 Functional Programming30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言